#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include <ctime>
#include <sstream>
#include "AnsiPrint.h"
#include "Vector.h"

const int test1 = 10000;
using namespace std;

class FooBar{
public:
    FooBar(string s="", int i=0) : foobar(s), barfoo(i){}
    string foobar;
    int barfoo;
};

/**
 * Print my id 
 */

void
PrintMyID(const char *studId) {
    stringstream ss;
    ss << "ID: " << studId;
    AnsiPrint(ss.str().c_str(), yellow, red, true, true);
    cout << endl << endl;

}


/**
 * PrintUsage
 */

void
PrintUsage(const char *progName) {

    cout << "Usage: " << progName << " intfile [charfile]" << endl;
}


// main testing routine
int main( int argc, char **argv ) 
{
    istream *is1=&cin;

    if (argc>2) {
        PrintUsage(argv[0]);
        exit(-1);
    }

    if (argc>1) {
        is1 = new ifstream(argv[1]);
        if (!(*is1)) {
            cerr << "Can't open file " << argv[1] << endl;
            exit(2);
        }
    }

    // Test read file and insert into vector then print it
    Vector<int> intV;
    int element,position=0;
    while( *is1 >> element ){
        intV.insert(position++,element);
    }
    cout<<"Test read file: ";
    while(position>0){
        cout<<intV[--position];
        intV.erase(position);
    }
    cout<<endl;

    // Test array index out of bounds exception
    cout<<"Test array index out of bounds exception: " << endl;
    try{
        intV.erase(-5566);
    }
    catch(const Exception & s){
        cout<<s.getMessage()<<endl;
    }
    try{
        intV.insert(5566, -5566);
    }
    catch(const Exception & s){
        cout<<s.getMessage()<<endl;
    }
    try{
        intV.erase(0);
    }
    catch(const Exception & s){
        cout<<s.getMessage()<<endl;
    }
    try{
        intV.insert(-1, -5566);
    }
    catch(const Exception & s){
        cout<<s.getMessage()<<endl;
    }

    intV.insert(0, 0);

    try{
        cout << intV[-1];
    }
    catch(const Exception & s){
        cout<<s.getMessage()<<endl;
    }

    try{
        cout << intV[5566];
    }
    catch(const Exception & s){
        cout<<s.getMessage()<<endl;
    }

    try{
        intV[-1] = 3;
    }
    catch(const Exception & s){
        cout<<s.getMessage()<<endl;
    }

    try{
        intV[5566] = 4;
    }
    catch(const Exception & s){
        cout<<s.getMessage()<<endl;
    }

    intV.clear();

    // Test get and set manipulate
    for ( int i = 0; i < 10; ++i )
        intV.insert( i, i );
    for ( int i = 0; i < 10; ++i )
        intV[i] = 10 - intV[i] - 1;

    // Test default constructor and copy constructor
    Vector<int> intV1;
    Vector<int>* intV2 = new Vector<int>( intV );
    Vector<int>& intV3 = intV1;

    // Test clear
    intV3.clear();
    cout << "Test clear: ";
    cout << ( intV1.size() == 0 ? "Success" : "Failure" ) << endl;

    // Test swap
    intV2->swap( intV3 );
    delete intV2;
    cout << "Test swap: ";
    for ( int i = 0; i < intV3.size(); ++i )
        cout << intV3[i];
    cout << endl;

    // Test assignment
    intV2 = new Vector<int>( intV );
    intV3 = intV = *intV2;
    delete intV2;
    cout << "Test assignment: ";
    for ( int i = 0; i < intV1.size(); ++i )
        cout << intV1[ i ];
    cout << endl;

    // Test const
    const Vector<int> intV4( intV1 );
    int dummy = intV4.size();
    dummy = intV4.capacity();
    dummy = intV4[0];
    intV1 = intV4;
    Vector<int> intVT( intV4 );

    // Use insertion sort to test insert and erase
    srand( time( NULL ) );
    Vector<int> intArray(1);
    // Initial random input array
    for ( int i = 0; i < test1; ++i ) {
        int        number = rand();
        int        position = rand() % ( intArray.size() + 1 );
        intArray.insert( position, number );
    }
    // Insertion sort
    for ( int i = 1; i < intArray.size(); ++i ) {
        int        j, t = intArray[ i ];

        for ( j = 0; j < i; ++j )
            if ( intArray[ i ] < intArray[ j ] )
                break;
        intArray.erase( i );
        intArray.insert( j, t );
    }
    // Test sorted property
    cout << "Test insert and erase: ";
    bool flag = true;
    for ( int i = 0; i < intArray.size() - 1; ++i )
        if ( intArray[ i ] > intArray[ i + 1 ] ) {
            flag = false;
            break;
        }
    cout << ( flag ? "Success" : "Failure" ) << endl;

    // Robust test
    cout << "Test Robustness: " ;
    for ( int i = 0; i < test1; ++i ) {
        Vector<int> intV6( 1 );
        //        Vector<int> intVarray[ 100 ] = { intV6 };
        for ( int j = 0; j < test1; ++j )
            intV6.insert( j, j );
        Vector<int> *intV7 = new Vector<int>;
        *intV7 = intV6;
        delete intV7;
    }

    cout << "done." << endl;

    cout << "Test different data type..." << endl;

    Vector< Vector<FooBar> > foobarplayer;

    for(int i = 0; i < 50; i++){
        Vector<FooBar> foo;
        for(int j = 0; j < 10; j++){
            stringstream ss;
            ss << "FB-" << i << "-" << j;    
            foo.insert(j, FooBar(ss.str(), j));
        }
        foobarplayer.insert(i, foo);
    }

    for(int i = 0; i < 10; i++){
        for(int j = 0; j < 10; j++){
            cout << foobarplayer[i][j].foobar << " " << foobarplayer[i][j].barfoo << " ";
        }
        cout << endl;
    }

    cout << "done." << endl;

    PrintMyID("94703029");
}

